home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Info-Mac 3
/
Info_Mac_1994-01.iso
/
Development
/
Information
/
Mac Programming Secrets 1.0.1
/
Chapter 04
/
Progress Indicator.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-05-19
|
8KB
|
254 lines
#include "Progress Indicator.h"
#include "Standard Stuff.h" // for "gMac"
/*******************************************************************************
Progress indicator routines
*******************************************************************************/
/*******************************************************************************
Private routines for the progress indicator routines. You’ll never have to
set or look at these values. The first three variables are initialized by
calling InitProgressIndicator. The remaining variables are used
internally.
*******************************************************************************/
GrafPtr pProgressPort; /* Port to be used for drawing. When updating the
indicator, we temporarily switch to this port,
restoring the old port when we are done. */
Rect pRect; /* Rectangle used for the progress indicator. The
entire indicator is drawn within this rectangle.
pRect is used to frame the indicator, and is then
inset by a pixel to draw the insides. */
long pMax; /* Maximum value for the process. This maximum can be
any value. The indicator is drawn such that the
ratio of the completed part to the entire part is
the same as the current value to the max value. */
long pCurrent; /* Current value. Set to zero when you call
InitProgressIndicator. It is updated by calling
SetProgress or SetProgressDelta. */
long pLastCurrent; /* The last value of pCurrent that was used to draw
the indicator. This is used to check if we need to
do any drawing; if pLastCurrent == pCurrent, we
don’t need to draw anything. */
short pLastBorder; /* The location of the division between the “completed”
part and the “to be completed” part of our indicator
the last time we drew it. We use this internally
(along with pLastCurrent) for determining if the
indicator needs to be updated. */
/*******************************************************************************
InitProgressIndicator
Call this just before you start your periodic task. All you have to do is
tell the progress indicator routines what port you want to draw in, the
rectangle you want to use, and the maximum value for your indicator.
Later, you’ll make repeated calls to update your progress value (which can
be anything from zero to the maximum value), which will update the
“thermometer” in the window.
*******************************************************************************/
void InitProgressIndicator(GrafPtr ourPort, Rect r, long max)
{
pProgressPort = ourPort;
pRect = r;
pMax = max;
pCurrent = 0;
pLastCurrent = 0;
pLastBorder = 0;
}
/*******************************************************************************
SetProgress
Call this periodically to update the indicator. Pass in the new value that
the indicator is supposed to represent. The value passed in will be
clipped between 0 and the maximum value.
*******************************************************************************/
void SetProgress(long absoluteAmount)
{
pCurrent = absoluteAmount;
DrawProgressBar();
}
/*******************************************************************************
SetProgressDelta
Call this periodically to update the indicator. Pass in a delta value to
be added to the current value. The result will be clipped between 0 and
the maximum value. The indicator will them be updated to indicate the new
current value.
This routine returns TRUE if we hit or passed the maximum value. This
could be handy to determine if we are done doing whatever periodic task we
are representing.
*******************************************************************************/
Boolean SetProgressDelta(long delta)
{
pCurrent += delta;
DrawProgressBar();
return (pCurrent >= pMax);
}
/*******************************************************************************
DrawProgressBar
Internal routine to update internal variables after the client has called
SetProgress() or SetProgressDelta(). It calculates the width of the new
rectangle; if it’s changed since the last time, we call our update routine.
*******************************************************************************/
void DrawProgressBar()
{
short border;
short rectWidth;
if ((pLastCurrent != pCurrent) && (pCurrent <= pMax)) {
pLastCurrent = pCurrent;
rectWidth = pRect.right - pRect.left - 2;
border = pRect.left + 1 + rectWidth * pCurrent / pMax;
if (pLastBorder != border) {
pLastBorder = border;
UpdateProgressBar();
}
}
}
/*******************************************************************************
UpdateProgressBar
Performs the actual drawing of the indicator. This routine is called
internally from other progress indicator routine to draw the new state of
the indicator. However, it can also be called in response to update events
by outside clients.
*******************************************************************************/
void UpdateProgressBar()
{
GrafPtr oldPort;
RGBColor oldForeColor;
RGBColor oldBackColor;
Rect doneRect;
Rect toDoRect;
doneRect = pRect;
InsetRect(&doneRect, 1, 1);
toDoRect = doneRect;
doneRect.right = toDoRect.left = pLastBorder;
GetPort(&oldPort);
SetPort(pProgressPort);
GetForeColor(&oldForeColor);
GetBackColor(&oldBackColor);
PenNormal();
SetFrameColor();
FrameRect(&pRect);
SetDoneColor();
PaintRect(&doneRect);
SetToDoColor();
PaintRect(&toDoRect);
RGBForeColor(&oldForeColor);
RGBBackColor(&oldBackColor);
SetPort(oldPort);
}
/*******************************************************************************
Progress indicator color routines.
These are called to set the colors for the three different parts of the
progress indicator: the frame, the “Done” part (the stuff on the left side
of the indicator), and the “To Do” part (the stuff on the right).
Note that we only check to see if we have Color QuickDraw or not; we don’t
also make a check to see how deep our monitor is. Instead, we simply let
QuickDraw match the color we specify to one most closely matching it on
whatever screen we’re drawing.
However, if we only set the foreground color, we run headfirst into
QuickDraw as it tries to help us out behind our backs. Take the case of
setting the “To Do” color. For that color, we use red = 0xCCCC, green =
0xCCCC, and blue = 0xFFFF. This gives us a nice steel blue color in 8-bit
mode, and a nice light gray in 4- and 2- bit modes. However, it draws as
black in 1-bit mode, when we expect and need it to draw in white.
What’s happening is this: when QuickDraw takes the color you specify and
tries to match it to one of the colors in the screen device’s color table,
it makes a special check to see if it maps your color into the background
color. If so, it inverts the color and tries again. In our case, our steel
blue gets mapped into white, which is the background color. QuickDraw says
“Oops! I’m about to draw something that won’t show up, so I’ll use a
different color, instead!” So it changes {0xCCCC, 0xCCCC, 0xFFFF} into
{0x3333, 0x3333, 0x0000} and remaps it. This time, the color maps into
black, which is what QuickDraw ends up using.
To avoid this feature, we take control of the background color. When we
want our light colors to map into white, we change the background color to
black, avoiding any conflict. Alternatively, we could attach a palette to
our window containing the two colors we want as “courteous” entries. This
is sufficient to get QuickDraw to stop being so helpful.
*******************************************************************************/
const RGBColor kBlack = {0x0000, 0x0000, 0x0000};
const RGBColor kWhite = {0xFFFF, 0xFFFF, 0xFFFF};
const RGBColor kDarkGrey = {0x4000, 0x4000, 0x4000};
const RGBColor kSteelBlue = {0xCCCC, 0xCCCC, 0xFFFF};
void SetFrameColor()
{
if (gMac.hasColorQD) {
RGBForeColor(&kBlack);
RGBBackColor(&kWhite);
} else {
PenPat(qd.black);
}
}
void SetDoneColor()
{
if (gMac.hasColorQD) {
RGBForeColor(&kDarkGrey);
RGBBackColor(&kWhite);
} else {
PenPat(qd.black);
}
}
void SetToDoColor()
{
if (gMac.hasColorQD) {
RGBForeColor(&kSteelBlue);
RGBBackColor(&kBlack);
} else {
PenPat(qd.white);
}
}